package butines.game.lastcolony.entities;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import butines.core.GameImage;
import butines.game.lastcolony.LastColony;

public class Game {

    private LastColony lastColony;
   
    private String type;
    private EntityTeam team;

    private int gridSize = 20;
    private int canvasWidth;
    private int canvasHeight;
    private int offsetX, offsetY;
    private boolean running;

    private int gameSpeed = 10;
    
    private int panningThreshold = 20; // Distance from edge of canvas at which panning starts
    private int panningSpeed = 10; // Pixels to pan every drawing loop
    
    private GameImage currentMapImage;
    private Level currentLevel;

    private int counter = 1;
    private List<Entity> items = new ArrayList<Entity>();
    private List<Building> buildings = new ArrayList<Building>();
    private List<Vehicle> vehicles = new ArrayList<Vehicle>();
    private List<Aircraft> aircraft = new ArrayList<Aircraft>();
    private List<Terrain> terrain = new ArrayList<Terrain>();
    private List<Entity> triggeredEvents = new ArrayList<Entity>();
    private List<Entity> selectedItems = new ArrayList<Entity>();
    private List<Entity> sortedItems = new ArrayList<Entity>();
    
    private Color selectionBorderColor = new Color(255, 255, 0, 128);
    private Color selectionFillColor = new Color(255, 215, 0, 51);
    private Color healthBarBorderColor = new Color(0, 0, 0, 204);
    private Color healthBarHealthyFillColor = new Color(0, 255, 0, 128);
    private Color healthBarDamagedFillColor = new Color(255, 0, 0, 128);
    private int lifeBarHeight = 5;
    
    
    private float speedAdjustmentFactor = 1f/64f;
    private float turnSpeedAdjustmentFactor = 1f/8f;
    
    private int[][] currentMapTerrainGrid;
    private int[][] currentMapPassableGrid;
    private boolean rebuildMapPassableGrid;
    
    public Game(LastColony lastColony) {
        this.lastColony = lastColony;
    }
    
    public void handlePanning(int mouseX, int mouseY) {
        if (mouseX <= panningThreshold) {
            if (offsetX >= panningSpeed) {
                offsetX -= panningSpeed;
            }
        } else if (mouseX >= canvasWidth - panningThreshold) {
            if (offsetX + canvasWidth + panningSpeed <= currentMapImage.getWidth()) {
                offsetX += panningSpeed;
            }
        }
        if (mouseY <= panningThreshold) {
            if (offsetY >= panningSpeed) {
                offsetY -= panningSpeed;
            }
        } else if (mouseY >= canvasHeight - panningThreshold) {
            if (offsetY + canvasHeight + panningSpeed <= currentMapImage.getHeight()) {
                offsetY += panningSpeed;
            }
        }
    }
        
    public void start() {
        running = true;
        lastColony.getGameInterfaceScreen().show();
    }
    
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }


    public EntityTeam getTeam() {
        return team;
    }

    public void setTeam(EntityTeam team) {
        this.team = team;
    }

    public GameImage getCurrentMapImage() {
        return currentMapImage;
    }

    public void setCurrentMapImage(GameImage currentMapImage) {
        this.currentMapImage = currentMapImage;
    }

    public Level getCurrentLevel() {
        return currentLevel;
    }

    public void setCurrentLevel(Level currentLevel) {
        this.currentLevel = currentLevel;
    }

    public int getGridSize() {
        return gridSize;
    }

    public void setGridSize(int gridSize) {
        this.gridSize = gridSize;
    }

    public int getOffsetX() {
        return offsetX;
    }

    public void setOffsetX(int offsetX) {
        this.offsetX = offsetX;
    }

    public int getOffsetY() {
        return offsetY;
    }

    public void setOffsetY(int offsetY) {
        this.offsetY = offsetY;
    }

    public int getCanvasWidth() {
        return canvasWidth;
    }

    public void setCanvasWidth(int canvasWidth) {
        this.canvasWidth = canvasWidth;
    }

    public int getCanvasHeight() {
        return canvasHeight;
    }

    public void setCanvasHeight(int canvasHeight) {
        this.canvasHeight = canvasHeight;
    }

    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    public int getPanningThreshold() {
        return panningThreshold;
    }

    public void setPanningThreshold(int panningThreshold) {
        this.panningThreshold = panningThreshold;
    }

    public int getPanningSpeed() {
        return panningSpeed;
    }

    public void setPanningSpeed(int panningSpeed) {
        this.panningSpeed = panningSpeed;
    }
    
    public List<Entity> getItems() {
		return items;
	}

	public List<Entity> getSelectedItems() {
        return selectedItems;
    }

    public void setSelectedItems(List<Entity> selectedItems) {
        this.selectedItems = selectedItems;
    }

    public Color getSelectionBorderColor() {
        return selectionBorderColor;
    }

    public void setSelectionBorderColor(Color selectionBorderColor) {
        this.selectionBorderColor = selectionBorderColor;
    }

    public Color getSelectionFillColor() {
        return selectionFillColor;
    }

    public void setSelectionFillColor(Color selectionFillColor) {
        this.selectionFillColor = selectionFillColor;
    }

    public Color getHealthBarBorderColor() {
        return healthBarBorderColor;
    }

    public void setHealthBarBorderColor(Color healthBarBorderColor) {
        this.healthBarBorderColor = healthBarBorderColor;
    }

    public Color getHealthBarHealthyFillColor() {
        return healthBarHealthyFillColor;
    }

    public void setHealthBarHealthyFillColor(Color healthBarHealthyFillColor) {
        this.healthBarHealthyFillColor = healthBarHealthyFillColor;
    }

    public Color getHealthBarDamagedFillColor() {
        return healthBarDamagedFillColor;
    }

    public void setHealthBarDamagedFillColor(Color healthBarDamagedFillColor) {
        this.healthBarDamagedFillColor = healthBarDamagedFillColor;
    }

    public int getLifeBarHeight() {
        return lifeBarHeight;
    }

    public void setLifeBarHeight(int lifeBarHeight) {
        this.lifeBarHeight = lifeBarHeight;
    }
    
    public float getSpeedAdjustmentFactor() {
        return speedAdjustmentFactor;
    }

    public void setSpeedAdjustmentFactor(float speedAdjustmentFactor) {
        this.speedAdjustmentFactor = speedAdjustmentFactor;
    }

    public float getTurnSpeedAdjustmentFactor() {
        return turnSpeedAdjustmentFactor;
    }

    public void setTurnSpeedAdjustmentFactor(float turnSpeedAdjustmentFactor) {
        this.turnSpeedAdjustmentFactor = turnSpeedAdjustmentFactor;
    }

    public int getGameSpeed() {
        return gameSpeed;
    }

    public void setGameSpeed(int gameSpeed) {
        this.gameSpeed = gameSpeed;
    }
    
    public int[][] getCurrentMapTerrainGrid() {
        return currentMapTerrainGrid;
    }

    public void setCurrentMapTerrainGrid(int[][] currentMapTerrainGrid) {
        this.currentMapTerrainGrid = currentMapTerrainGrid;
    }

    public int[][] getCurrentMapPassableGrid() {
        return currentMapPassableGrid;
    }

    public void setCurrentMapPassableGrid(int[][] currentMapPassableGrid) {
        this.currentMapPassableGrid = currentMapPassableGrid;
    }

    public void reset() {
        counter = 1;
        items.clear();
        buildings.clear();
        vehicles.clear();
        aircraft.clear();
        terrain.clear();
        triggeredEvents.clear();
        selectedItems.clear();
        sortedItems.clear();
    }
    
    public Entity add(Entity entity) {
        // Set a unique id for the item
        if (entity.getUID() == 0) {
            entity.setUID(counter++);
            entity.setGame(this);
        }

        // Add the item to the items array
        items.add(entity);
        sortedItems.add(entity);
        
        // Add the item to the type specific array
        if (entity instanceof Building) {
            buildings.add((Building) entity);
            rebuildMapPassableGrid = true;
        }
        if (entity instanceof Aircraft) {
            aircraft.add((Aircraft) entity);
        }
        if (entity instanceof Terrain) {
            terrain.add((Terrain) entity);
            rebuildMapPassableGrid = true;
        }
        if (entity instanceof Vehicle) {
            vehicles.add((Vehicle) entity);
        }
        
        return entity;
    }
    
    public void remove(Entity entity) {
        // Unselect item if it is selected
        entity.setSelected(false);
        
        selectedItems.remove(entity);

        items.remove(entity);
        sortedItems.remove(entity);
        
        if (entity instanceof Building) {
            buildings.remove((Building) entity);
            rebuildMapPassableGrid = true;
        }
        if (entity instanceof Aircraft) {
            aircraft.remove((Aircraft) entity);
        }
        if (entity instanceof Terrain) {
            terrain.remove((Terrain) entity);
            rebuildMapPassableGrid = true;
        }
        if (entity instanceof Vehicle) {
            vehicles.remove((Vehicle) entity);
        }
    }
    
    final Comparator<Entity> sorted = new Comparator<Entity>() {
        @Override
        public int compare(Entity o1, Entity o2) {
            return (int) (o1.getY() - o2.getY() + ( (o2.getY() == o1.getY()) ? (o1.getX() - o2.getX()) : 0));
        }
    };
    
    public void animationLoop() {

        // Process orders for any item that handles it
        for (int i = items.size() - 1; i >= 0; i--) {
            items.get(i).processOrders();
        }
        
        // Animate each of the elements within the game
        for (int i = items.size() - 1; i >= 0; i--) {
            items.get(i).animate();
        }

        // Sort game items into a sortedItems array based on their x,y coordinates
        Collections.sort(sortedItems, sorted);
    }
    
    public void drawingLoop(Graphics2D g2d) {
        
        currentMapImage.draw(g2d, 0, 0, offsetX, offsetY, canvasWidth, canvasHeight);

        // Start drawing the foreground elements
        for (Entity entity : sortedItems) {
            entity.draw(g2d);
        }
        
        g2d.setColor(new Color(245, 0, 0, 75));
        for (int y = 0; y < currentMapPassableGrid.length; y++) {
            for (int x = 0; x < currentMapPassableGrid[y].length; x++) {
                if (currentMapPassableGrid[y][x] == 1) {
                    g2d.fillRect(x*gridSize-offsetX , y*gridSize-offsetY, gridSize-1, gridSize-1);
                }
            }
        }
        
     }

    public void clearSelection() {
        for (int i = selectedItems.size() - 1; i >= 0; i--) {
            selectedItems.remove(i).setSelected(false);
        }
    }
    
    public void selectItem(Entity item, boolean shiftPressed) {
        // Pressing shift and clicking on a selected item will deselect it
        if (shiftPressed && item.isSelected()) {
            // deselect item
            item.setSelected(false);
            for (int i = selectedItems.size() - 1; i >= 0; i--) {
                if (selectedItems.get(i).getUID() == item.getUID()) {
                    selectedItems.remove(i);
                    break;
                }
            }
            return;
        }
        if (item.isSelectable() && !item.isSelected()) {
            item.setSelected(true);
            selectedItems.add(item);
        }
    }
    
    public Entity getItemByUID(int uid) {
        for (int i = items.size() - 1; i >= 0; i--) {
            if (items.get(i).getUID() == uid) {
                return items.get(i);
            }
        }
        return null;
    }

    public void sendCommand(List<Integer> uids, GameCommand cmd) {
        if (type.equals("singleplayer")) {
            lastColony.getSinglePlayer().sendCommand(uids, cmd);
        } else {
            lastColony.getMultiPlayer().sendCommand(uids, cmd);
        }
    }
    
    // Receive command from singleplayer or multiplayer object and send it to units
    public void processCommand(List<Integer> uids, GameCommand cmd) {
        // In case the target "to" object is in terms of uid, fetch the target object
        Entity toObject = null;
        if (cmd.getToUID() > 0) {
            toObject = getItemByUID(cmd.getToUID());
            if (toObject == null || toObject.isDead()) {
                // To object no longer exists. Invalid command
                return;
            }
        }
        for (int uid : uids) {
            Entity item = getItemByUID(uid);
            // if uid is a valid item, set the order for the item
            if (item != null) {
                item.getOrders().setType(cmd.getType());
                item.getOrders().setToX(cmd.getToX());
                item.getOrders().setToY(cmd.getToY());
                item.getOrders().setTo(toObject);
            }
        }
    }

    public void rebuildPassableGrid() {
        if (rebuildMapPassableGrid == false) {
            return;
        }
        
       
        for (int y = 0; y < currentMapPassableGrid.length; y++) {
            for (int x = 0; x < currentMapPassableGrid[y].length; x++) {
                currentMapPassableGrid[y][x] = currentMapTerrainGrid[y][x];
            }
        }
        
        for (int i = items.size() - 1; i >= 0; i--) {
            Entity item = items.get(i);
            if (item instanceof Building || item instanceof Terrain) {
                int[][] passabledGrid = item.getPassableGrid();
                for (int y = passabledGrid.length - 1; y >= 0; y--) {
                    for (int x = passabledGrid[y].length - 1; x >= 0; x--) {
                        if (passabledGrid[y][x] == 1) {
                            currentMapPassableGrid[(int) item.getY() + y][(int) item.getX() + x] = 1;
                        }
                    }
                }
            }
        }
        
        rebuildMapPassableGrid = false;
    }
    
}
